/*
 * ArchivoFrontCoding.cpp
 *Es frontcoding parcial... por eso el tamanio del bloque.
 *  Created on: 02/06/2013
 *      Author: jonathan
 */
/**
 * El archivo se escribe:
 * El front coding de la palabra.
 * La frecuencia (Cantidad de documentos donde aparece)
 * El puntero al archivo de posiciones.
 *
 *
 */
#include "ArchivoFrontCoding.h"
#include "../../commons/PathsStore.h"
#include "../../commons/ArchivoUtils.h"
#include "../../commons/InfoRepositorioActual.h"
#include "../estructuras/TerminoProxy.h"
#include <exception>
ArchivoFrontCoding::ArchivoFrontCoding(fstream::openmode mode) {
	this->archPtros = new ArchivoPunteros(mode);
	PathsStore ps;
	this->tamanioBloque
			= InfoRepositorioActual::Instance()->getTamanioBloqueFC();
	this->bs.open(ps.getPathFrontCodding(), mode);
	this->contador = 0;
	this->compresor = new Compresor(&(this->bs));
}

ArchivoFrontCoding::ArchivoFrontCoding(FileName archivoFC,
		FileName archivoPtros, EnteroLargo tamanioBloque,
		fstream::openmode mode) {
	this->archPtros = new ArchivoPunteros(archivoPtros, mode);
	this->bs.open(archivoFC, mode);
	this->tamanioBloque = tamanioBloque;
	this->contador = 0;
	this->compresor = new Compresor(&(this->bs));
}

ArchivoFrontCoding::~ArchivoFrontCoding() {
	delete this->archPtros;
}

EntradaIndice * ArchivoFrontCoding::escribir(Termino & termino) {
	EnteroLargo ptroLexico = 0;
	bool ppal = false;
	//si es el primero o me fui del bloque.
	if (this->contador == this->tamanioBloque || this->contador == 0) {
		ptroLexico = this->resetFrontCoding();
		ppal = true;
		this->contador =0;
	}

	//FRONTCODING
	EntradaIndice * entrada =
			this->escribirFrontCoding(termino.getCaracteres());

	//FRECUENCIA
	this->compresor->escribirDelta(termino.getDocumentos()->size());

	//PUNTEROS
	PunteroPosiciones posicionPunteros = this->archPtros->escribir(
			*(termino.getDocumentos()), termino.getB());

	//escribe la frecuencia de documentos
	//podria ir comprimido pero el bs deberia permitir escribir byte y bits intercalado.
	escribirNumeroVariableLargo(&(this->bs), posicionPunteros, 7);

	//TODO SI MANEJARA OK BIT VS BYTE NO DEBERIA IR ESTE FILLBYTE {COMPRESION}
	this->bs.fillByte();
	entrada->setPrincipal(ppal);
	entrada->setPtroLexico(ptroLexico);
	this->contador++;
	this->ultimoTermino = termino.getCaracteres();


    InfoRepositorioActual::Instance()->setCantidadUltimoBloque(contador);

	return entrada;

}

EnteroLargo ArchivoFrontCoding::resetFrontCoding() {
    InfoRepositorioActual::Instance()->aumentarBloquesFC();
	this->ultimoTermino = "";
	this->bs.fillByte();
	this->contador = 0;
	return this->bs.tellg();
}

EntradaIndice * ArchivoFrontCoding::escribirFrontCoding(string termino) {
	EntradaIndice *entradaIndice = new EntradaIndice();
	if (this->ultimoTermino == "") {
		entradaIndice->setCharsDistintos(termino.size());
		entradaIndice->setCharsIguales(0);
		this->escribirCadena(termino);
	} else {
		//comparo caracter a caracter;
		int cont = 0;
		string::iterator itCad1 = this->ultimoTermino.begin();
		string::iterator itCad2 = termino.begin();
		while ((*itCad1 == *itCad2) && itCad1 != this->ultimoTermino.end()
				&& itCad2 != termino.end()) {
			cont++;
			itCad2++;
			itCad1++;
		}
		entradaIndice->setCharsIguales(cont);
		entradaIndice->setCharsDistintos(termino.size() - cont);
		//escribo lo que es distinto al anterior.
		this->escribirCadena(termino.substr(cont, termino.size()));
	}

	return entradaIndice;
}

Termino * ArchivoFrontCoding::leerCabecera(EntradaIndice &entrada) {
	string texto = "";
	TerminoProxy * termino = new TerminoProxy(this->archPtros);

	this->bs.seekg(entrada.getPtroLexico());
	//es cabecera asi que va a tener todos los terminos.
	//no estoy manejando compresion en caracteres aun
//	char * buffer = new char[entrada.getCharsDistintos()];
//	bs.read(buffer, entrada.getCharsDistintos());
//	cout<< buffer << endl;
	string term = "";
	char c;
	for (int i = 0; i < entrada.getCharsDistintos(); i++) {
		bs.get(c);
		term.push_back(c);
	}
	termino->setCaracteres(term);

	// leo la frecuencia del termino, para q el proxy pueda leer del archivo de ptros
	termino->setFt(this->compresor->leerDelta());

	//Leo el puntero de posiciones TODO MIRAR SI RETORNA UN VALOR DE 64 BITS COMO CORRESPONDE.
	termino->setPtro(leerNumeroVariableLargo(&bs,7));
	this->bs.omitByte();
	//TODO {COMPRESION}
	//y nada mas, lo unico que se retorna es el texto del termino, y una forma de resolver los documentos.
	return termino;

}
void ArchivoFrontCoding::escribirCadena(string cadena) {
	for (std::string::iterator it = cadena.begin(); it != cadena.end(); ++it) {
		this->bs.put(*it);
	}

}

string ArchivoFrontCoding::leerCaracteres(int cantCaracteres){
	string salida = "";
	for(int i=0; i<cantCaracteres; i++){
		salida.push_back(this->bs.get());
	}
	return salida;
}
void ArchivoFrontCoding::close() {
	this->bs.close();
	this->archPtros->close();
}


//Esto lee en base a la lectura anterior.
Termino * ArchivoFrontCoding::proximoTermino(EntradaIndice & entrada){
	TerminoProxy * termino = new TerminoProxy(this->archPtros);
	string palabra = this->ultimoTermino.substr(0,entrada.getCharsIguales());
	palabra.append(this->leerCaracteres(entrada.getCharsDistintos()));
	termino->setCaracteres(palabra);
	termino->setFt(this->compresor->leerDelta());
	termino->setPtro(leerNumeroVariableLargo(&bs,7));
	this->ultimoTermino = palabra;
	this->bs.omitByte();
	return termino;
}

//El primer elemento de la lista tiene que ser el ppal sino explota
map<string, Termino*> * ArchivoFrontCoding::leerBloque(list<EntradaIndice>& entradas){
	map<string, Termino*> * salida = new map<string, Termino*>();
	list<EntradaIndice>::iterator it = entradas.begin();
	if(!(*it).getPrincipal()){
		throw exception();
	}
	//posiciono.
	this->bs.seekg((*it).getPtroLexico());
	//leo
	Termino * term = NULL;
	for(it=entradas.begin(); it!= entradas.end();++it){
		term = this->proximoTermino(*it);
		salida->insert(pair<string, Termino*> (term->getCaracteres(), term));
	}
	return salida;
}
